﻿using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using ViewMaker.Core.ViewModels;
using System.Windows.Controls.Primitives;
using System.Collections.Generic;
using System.Windows.Markup;
using System.Diagnostics;
using ViewMaker.Core.Utils;
using System.Windows.Media.Imaging;

namespace ViewMaker.Core.Silverlight
{
    internal class SlViewLayoutViewConnector : DependencyObject
    {

        private ViewLayoutViewModel editor;
        private ContentControl content;
        private ContentControl targetContent;
        private StackPanel panel;
        private FrameworkElement view;
        private bool isLayoutActive;

        public SlViewLayoutViewConnector(ContentControl parent, ContentControl target, ViewLayoutViewModel editor)
        {
            this.editor = editor;
            this.content = parent;
            this.targetContent = target;
            this.targetContent.VerticalAlignment = VerticalAlignment.Top;
            panel = new StackPanel();
            panel.Orientation = Orientation.Horizontal;
            panel.VerticalAlignment = VerticalAlignment.Top;
            panel.Children.Add(target);
            var scroll = new ScrollViewer();
            scroll.Content = panel;
            scroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
            scroll.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            scroll.BorderThickness = new Thickness(0);
            content.Content = scroll;

            ConnectEditor();
        }

        private StackPanel CreateContentMenuItem(string uri, string text)
        {
            var item = new StackPanel { Orientation = Orientation.Horizontal };
            item.Margin = new Thickness(5);
            item.Children.Add(new Image { Source = new BitmapImage(new Uri("/SilverlightViewMaker.Core;component/Images/" + uri, UriKind.Relative)) });
            item.Children.Add(new TextBlock { Text = text });
            return item;
        }


        private void ConnectEditor()
        {
            view = (FrameworkElement)ViewUtil.BuildContent(editor, false);
            view.Visibility = Visibility.Collapsed;
            panel.Children.Add(view);

            var ctx = new ContextMenu();
            ContextMenuService.SetContextMenu(this.targetContent, ctx);

            var item1 = new MenuItem() { Header = CreateContentMenuItem("edit.png", "Edit Layout") };
            ctx.Items.Add(item1);
            item1.Command = new RelayCommand(delegate() { view.Visibility = Visibility.Visible; isLayoutActive = true; });

            ctx.Items.Add(new Separator());
            var item2 = new MenuItem() { Header = "Show XAML" };
            ctx.Items.Add(item2);
            item2.Command = editor.ShowXamlCommand;

            var item3 = new MenuItem() { Header = "Show Layout" };
            ctx.Items.Add(item3);
            item3.Command = editor.ShowLayoutViewerCommand;

            this.targetContent.Unloaded += new RoutedEventHandler(content_Unloaded);// 発生しないケースの対策が必要
            this.targetContent.GotFocus += new RoutedEventHandler(Content_GotFocus);
            this.editor.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(ViewModel_PropertyChanged);
            this.editor.ViewCommandNotified += new EventHandler<ViewCommandEventArgs>(ViewModel_ViewCommandNotified);
            Debug.WriteLine("Editor Loaded:" + targetContent.GetHashCode().ToString());
        }

        void content_Unloaded(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("Editor UnLoaded:" + targetContent.GetHashCode().ToString());   
            foreach (MenuItem item in ContextMenuService.GetContextMenu(this.targetContent).Items.OfType<MenuItem>())
            {
                item.Command = null;
            }
            ContextMenuService.SetContextMenu(this.targetContent, null);
            editor.Close();
            this.targetContent.Unloaded -= new RoutedEventHandler(content_Unloaded);
            this.targetContent.GotFocus -= new RoutedEventHandler(Content_GotFocus);
            this.editor.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(ViewModel_PropertyChanged);
            this.editor.ViewCommandNotified -= new EventHandler<ViewCommandEventArgs>(ViewModel_ViewCommandNotified);
            this.panel.Children.Clear();
            this.targetContent.DataContext = null;
            this.view.DataContext = null;
        }

        void Content_GotFocus(object sender, RoutedEventArgs e)
        {
            if (isLayoutActive) SyncViewModelTree(e.OriginalSource as DependencyObject);
        }

        private void SyncViewModelTree(DependencyObject obj)
        {
            if (obj == null) return;
            var fe = obj as FrameworkElement;
            if (fe != null && fe.Name != null)
            {
                if (editor.SelectByName(fe.Name)) return;
            }
            if (obj is UIElement)
            {
                SyncViewModelTree(VisualTreeHelper.GetParent(obj));
            }
        }


        void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "CurrentItem") UpdateMarker(editor.CurrentItem);
            if (e.PropertyName == "XamlCode") RefreshTarget();
        }

        void ViewModel_ViewCommandNotified(object sender, ViewCommandEventArgs e)
        {
            if (e.Command == "Hide")
            {
                isLayoutActive = false;
                view.Visibility = Visibility.Collapsed; 
                UpdateMarker(null);
            }
            else if (e.Command == "GetElement")
            {
                var para = e.Parameter as ViewLayoutViewModel.GetElementCommandInfo;
                if (para != null) para.Element = FindById(content, para.Name);
            }
        }

        #region マーカー関連

        private Thumb preThumb;
        private FrameworkElement preElement;
        private Brush preBackColor;
        private void UpdateMarker(ViewLayoutItem current)
        {
            ResetBackColorThumb();
            if (current != null)
            {
                var ele = FindById(targetContent, current.Name);
                if (ele != null && preElement != ele)
                {
                    preBackColor = GetBackgroundElement(ele);
                    SetBackgroundElement(ele, new SolidColorBrush(Colors.Yellow));
                    preElement = ele;
                    MakeInfoText(ele);
                    if (current.Control.IsCanvasElement) MakeCanvasThumb(ele);
                }
            }
        }

        private void ResetBackColorThumb()
        {
            if (preElement != null) SetBackgroundElement(preElement, preBackColor);
            if (preThumb != null) preThumb.Visibility = System.Windows.Visibility.Collapsed;
            preElement = null;
            preThumb = null;
            this.editor.InfoText = "";
        }

        private void SetBackgroundElement(FrameworkElement element, Brush color)
        {
            if (element is Control) ((Control)element).Background = color;
            if (element is Panel) ((Panel)element).Background = color;
        }

        private Brush GetBackgroundElement(FrameworkElement element)
        {
            if (element == null) return null;
            if (element is Control) return ((Control)element).Background;
            if (element is Panel) return ((Panel)element).Background;
            return null;
        }

        private void MakeCanvasThumb(FrameworkElement ele)
        {
            Canvas canvas = null;
            FrameworkElement target = ele;
            DependencyObject obj = ele.Parent;

            while (obj != null)
            {
                if (obj is Canvas)
                {
                    canvas = (Canvas)obj;
                    break;
                }
                if (obj is FrameworkElement)
                {
                    target = (FrameworkElement)obj;
                }
                obj = VisualTreeHelper.GetParent(obj);
            }
            if (canvas != null)
            {
                string thumbName = canvas.Name + "__CanvasThumb";
                var thumb = (Thumb)FindById(targetContent, thumbName);
                if (thumb == null)
                {
                    thumb = new Thumb() { Name = thumbName, Width = 10, Height = 10, Background = new SolidColorBrush(Colors.Yellow) };
                    canvas.Children.Add(thumb);
                    thumb.DragDelta += new DragDeltaEventHandler(thumb_DragDelta);
                }
                thumb.Visibility = System.Windows.Visibility.Visible;
                Canvas.SetTop(thumb, Canvas.GetTop(target) + target.ActualHeight - thumb.Height);
                Canvas.SetLeft(thumb, Canvas.GetLeft(target) + target.ActualWidth - thumb.Width);
                preThumb = thumb;
                preThumb.Tag = new List<FrameworkElement>(new FrameworkElement[] { target });
            }
        }

        private void MakeInfoText(FrameworkElement ele)
        {
            this.editor.InfoText = string.Format("SIZE: {0} x {1}",
                double.IsNaN(ele.ActualWidth) ? "*" : ele.ActualWidth.ToString("0.0"),
                double.IsNaN(ele.ActualHeight) ? "*" : ele.ActualHeight.ToString("0.0"));
        }


        void thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            var target = (Thumb)sender;
            Canvas.SetLeft(target, Canvas.GetLeft(target) + e.HorizontalChange);
            Canvas.SetTop(target, Canvas.GetTop(target) + e.VerticalChange);
            var list = (List<FrameworkElement>)target.Tag;
            foreach (var ele in list)
            {
                Canvas.SetLeft(ele, Canvas.GetLeft(ele) + e.HorizontalChange);
                Canvas.SetTop(ele, Canvas.GetTop(ele) + e.VerticalChange);
            }
            editor.CurrentItem.Control.CanvasLeft = (int)Canvas.GetLeft(list.First());
            editor.CurrentItem.Control.CanvasTop = (int)Canvas.GetTop(list.First());
        }


        private FrameworkElement FindById(FrameworkElement target, string name)
        {
            return target.FindById(name);
        }

        private delegate void UpdateMakerDelegate(ViewLayoutItem current);

        private void RefreshTarget()
        {
            var xamlString = editor.XamlCode;
            try
            {
                var root = xamlString == null ? null : (FrameworkElement)XamlReader.Load(xamlString);
                targetContent.Content = root;
                preThumb = null;
                preElement = null;
                this.Dispatcher.BeginInvoke(new UpdateMakerDelegate(UpdateMarker), editor.CurrentItem);
            }
            catch (XamlParseException ex)
            {
                Debug.WriteLine(ex.ToString());
                Debug.WriteLine(xamlString);

                var line = ex.LineNumber > 0 ? xamlString.Split('\n').Skip(ex.LineNumber - 1).First() : "";
                targetContent.Content = ex.Message + "\n" + line;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
                Debug.WriteLine(xamlString);
                targetContent.Content = ex.Message;
            }
        }

        #endregion
    }
}
